/* 
 * Copyright (C) 2001-2003 Jacek Sieka, j_s@telia.com
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "stdinc.h"
#include "DCPlusPlus.h"

#include "HubManager.h"

#include "ClientManager.h"
#include "CryptoManager.h"

#include "HttpConnection.h"
#include "StringTokenizer.h"
#include "SimpleXML.h"
#include "UserCommand.h"

#define FAVORITES_FILE "XML\\Favorites.xml"

void HubManager::onHttpFinished() throw() {
	string::size_type i, j;
	string* x;
	string bzlist;

	if(listType == TYPE_BZIP2) {
		try {
			CryptoManager::getInstance()->decodeBZ2((u_int8_t*)downloadBuf.data(), downloadBuf.size(), bzlist);
		} catch(const CryptoException&) {
			bzlist.clear();
		}
		x = &bzlist;
	} else {
		x = &downloadBuf;
	}

	{
		Lock l(cs);
		publicHubs.clear();
		i = 0;
		
		while( (i < x->size()) && ((j=x->find("\r\n", i)) != string::npos)) {
			StringTokenizer<string> tok(x->substr(i, j-i), '|');
			i = j + 2;
			if(tok.getTokens().size() < 4)
				continue;

			StringList::const_iterator k = tok.getTokens().begin();
			const string& name = *k++;
			const string& server = *k++;
			const string& desc = *k++;
			const string& users = *k++;
			publicHubs.push_back(HubEntry(name, server, desc, users));
		}
	}
	downloadBuf = Util::emptyString;
}

void HubManager::save() {
	if(dontSave)
		return;
	Lock l(cs);
	try {
		SimpleXML xml;

		xml.addTag("Favorites");
		xml.stepIn();

		xml.addTag("Hubs");
		xml.stepIn();

		for(FavoriteHubEntry::Iter i = favoriteHubs.begin(); i != favoriteHubs.end(); ++i) {
			xml.addTag("Hub");
			xml.addChildAttrib("Name", (*i)->getName());
			xml.addChildAttrib("Connect", (*i)->getConnect());
			xml.addChildAttrib("Description", (*i)->getDescription());
			xml.addChildAttrib("Nick", (*i)->getNick(false));
			xml.addChildAttrib("Password", (*i)->getPassword());
			xml.addChildAttrib("Server", (*i)->getServer());
			xml.addChildAttrib("UserDescription", (*i)->getUserDescription());
			// CDM EXTENSION BEGINS FAVS
			//Add to favs
			xml.addChildAttrib("RawOne", (*i)->getRawOne());
			xml.addChildAttrib("RawTwo", (*i)->getRawTwo());
			xml.addChildAttrib("RawThree", (*i)->getRawThree());
			xml.addChildAttrib("RawFour", (*i)->getRawFour());
			xml.addChildAttrib("RawFive", (*i)->getRawFive());
			xml.addChildAttrib("RawSix", (*i)->getRawSix());
			xml.addChildAttrib("RawSeven", (*i)->getRawSeven());
			xml.addChildAttrib("RawEight", (*i)->getRawEight());
			xml.addChildAttrib("RawNine", (*i)->getRawNine());
			xml.addChildAttrib("RawTen", (*i)->getRawTen());
			xml.addChildAttrib("OpChat", (*i)->getOpChat());
			xml.addChildAttrib("UserIp", (*i)->getUserIp());
			xml.addChildAttrib("CheckClients", (*i)->getCheckClients());
			xml.addChildAttrib("CheckFilelists", (*i)->getCheckFilelists());
			xml.addChildAttrib("CheckFakeShare", (*i)->getCheckFakeShare());
			xml.addChildAttrib("CheckOnConnect", (*i)->getCheckOnConnect());
			//Gabberworld start 
			xml.addChildAttrib("CountryCodeCheck", (*i)->getCountryCodeCheck());
			xml.addChildAttrib("CountryCodeRaw", (*i)->getCountryCodeRaw());
			//Gabberworld end
			//Sulan start
			xml.addChildAttrib("ProtectedUsers", (*i)->getProtectedUsers());
			xml.addChildAttrib("HideShare", (*i)->getHideShare());
			//Sulan start 28
			xml.addChildAttrib("Trigger", (*i)->getTrigger());
			//Sulan start svn86
			xml.addChildAttrib("LogChat", (*i)->getLogChat());
			//Sulan start svn101
			xml.addChildAttrib("ColumnOrder", (*i)->getColumnOrder());
			xml.addChildAttrib("ColumnWidth", (*i)->getColumnWidth());
			xml.addChildAttrib("ColumnVisible", (*i)->getColumnVisible());
			//Sulan start svn124
			xml.addChildAttrib("WindowPosX", (*i)->getWindowPosX());
			xml.addChildAttrib("WindowPosY", (*i)->getWindowPosY());
			xml.addChildAttrib("WindowSizeX", (*i)->getWindowSizeX());
			xml.addChildAttrib("WindowSizeY", (*i)->getWindowSizeY());
			xml.addChildAttrib("WindowType", (*i)->getWindowType());
			//End
			// pothead starts svn192 svn196 (sulan code)
			xml.addChildAttrib("HubMessage", (*i)->getHubMessage());
			xml.addChildAttrib("UseMyinfoDetect", (*i)->getUseMyinfoDetect());
			//Sulan start svn?
			xml.addChildAttrib("DisableSearchSelect", (*i)->getDisableSearchSelect());
			xml.addChildAttrib("RegStatus", (*i)->getRegStatus());
			xml.addChildAttrib("DisableRaw", (*i)->getDisableRaws());
			// pothead ends
			// CDM EXTENSION ENDS
		}
		xml.stepOut();
		xml.addTag("Users");
		xml.stepIn();
		for(User::Iter j = users.begin(); j != users.end(); ++j) {
			xml.addTag("User");
			xml.addChildAttrib("Nick", (*j)->getNick());
			xml.addChildAttrib("LastHubAddress", (*j)->getLastHubAddress());
			xml.addChildAttrib("LastHubName", (*j)->getLastHubName());
			xml.addChildAttrib("LastSeen", (*j)->getFavoriteLastSeen());
			xml.addChildAttrib("GrantSlot", (*j)->getFavoriteGrantSlot());
			xml.addChildAttrib("UserDescription", (*j)->getUserDescription());
		}
		xml.stepOut();
		xml.addTag("UserCommands");
		xml.stepIn();
		for(UserCommand::Iter k = userCommands.begin(); k != userCommands.end(); ++k) {
			if(!k->isSet(UserCommand::FLAG_NOSAVE)) {
				xml.addTag("UserCommand");
				xml.addChildAttrib("Type", k->getType());
				xml.addChildAttrib("Context", k->getCtx());
				xml.addChildAttrib("Name", k->getName());
				xml.addChildAttrib("Command", k->getCommand());
				xml.addChildAttrib("Hub", k->getHub());
			}
		}
		xml.stepOut();
		
		xml.stepOut();

		string fname = Util::getAppPath() + FAVORITES_FILE;

		File f(fname + ".tmp", File::WRITE, File::CREATE | File::TRUNCATE);
		f.write(SimpleXML::utf8Header);
		f.write(xml.toXML());
		f.close();
		File::deleteFile(fname);
		File::renameFile(fname + ".tmp", fname);

	} catch(const Exception& e) {
		dcdebug("HubManager::save: %s\n", e.getError().c_str());
	}
}

void HubManager::load() {
	
	// Add standard op commands
	static const char kickstr[] = 
		// CDM EXTENSION BEGINS
		//"$To: %[nick] From: %[mynick] $<%[mynick]> You are being kicked because: %[line:Reason]|<%[mynick]> %[mynick] is kicking %[nick] because: %[line:Reason]|$Kick %[nick]|";
		"$To: %[nick] From: %[mynick] $<%[mynick]> You are being kicked because: %[kickline:Reason]|<%[mynick]> %[mynick] is kicking %[nick] because: %[kickline:Reason] %[file]|$Kick %[nick]|";
		// CDM EXTENSION ENDS
	addUserCommand(UserCommand::TYPE_RAW_ONCE, UserCommand::CONTEXT_CHAT | UserCommand::CONTEXT_SEARCH, UserCommand::FLAG_NOSAVE, 
		STRING(KICK_USER), kickstr, "op");
	// pothead extension starts (R2 Code) #7 and svn51
	static const char kickfilestr[] = 
		"$To: %[nick] From: %[mynick] $<%[mynick]> You are being kicked because: %[kickline:Reason] %[file]|<%[mynick]> is kicking %[nick] because: %[kickline:Reason] %[file]|$Kick %[nick]|";
	addUserCommand(UserCommand::TYPE_RAW_ONCE, UserCommand::CONTEXT_SEARCH | UserCommand::CONTEXT_FILELIST, UserCommand::FLAG_NOSAVE, 
		STRING(KICK_USER_FILE), kickfilestr, "op");
	// pothead extension ends (R2 Code)
	static const char redirstr[] =
		"$OpForceMove $Who:%[nick]$Where:%[line:Target Server]$Msg:%[line:Message]|";
	addUserCommand(UserCommand::TYPE_RAW_ONCE, UserCommand::CONTEXT_CHAT | UserCommand::CONTEXT_SEARCH, UserCommand::FLAG_NOSAVE, 
		STRING(REDIRECT_USER), redirstr, "op");

	try {
		SimpleXML xml;
		xml.fromXML(File(Util::getAppPath() + FAVORITES_FILE, File::READ, File::OPEN).read());
		
		if(xml.findChild("Favorites")) {
			xml.stepIn();
			load(&xml);
			xml.stepOut();
		}
	} catch(const Exception& e) {
		dcdebug("HubManager::load: %s\n", e.getError().c_str());
	}
}

void HubManager::load(SimpleXML* aXml) {
	dontSave = true;

	// Old names...load for compatibility.
	aXml->resetCurrentChild();
	if(aXml->findChild("Favorites")) {
		aXml->stepIn();
		while(aXml->findChild("Favorite")) {
			FavoriteHubEntry* e = new FavoriteHubEntry();
			e->setName(aXml->getChildAttrib("Name"));
			e->setConnect(aXml->getBoolChildAttrib("Connect"));
			e->setDescription(aXml->getChildAttrib("Description"));
			e->setNick(aXml->getChildAttrib("Nick"));
			e->setPassword(aXml->getChildAttrib("Password"));
			e->setServer(aXml->getChildAttrib("Server"));
			e->setUserDescription(aXml->getChildAttrib("UserDescription"));
			// CDM EXTENSION BEGINS FAVS
			//Add to favs
			e->setRawOne(aXml->getChildAttrib("RawOne"));
			e->setRawTwo(aXml->getChildAttrib("RawTwo"));
			e->setRawThree(aXml->getChildAttrib("RawThree"));
			e->setRawFour(aXml->getChildAttrib("RawFour"));
			e->setRawFive(aXml->getChildAttrib("RawFive"));
			e->setRawSix(aXml->getChildAttrib("RawSix"));
			e->setRawSeven(aXml->getChildAttrib("RawSeven"));
			e->setRawEight(aXml->getChildAttrib("RawEight"));
			e->setRawNine(aXml->getChildAttrib("RawNine"));
			e->setRawTen(aXml->getChildAttrib("RawTen"));
			e->setOpChat(aXml->getChildAttrib("OpChat"));
			e->setUserIp(aXml->getBoolChildAttrib("UserIp"));
			e->setCheckClients(aXml->getBoolChildAttrib("CheckClients"));
			e->setCheckFilelists(aXml->getBoolChildAttrib("CheckFilelists"));
			e->setCheckFakeShare(aXml->getBoolChildAttrib("CheckFakeShare"));
			e->setCheckOnConnect(aXml->getBoolChildAttrib("CheckOnConnect"));
			// CDM EXTENSION ENDS
			favoriteHubs.push_back(e);
		}
		aXml->stepOut();
	}
	aXml->resetCurrentChild();
	if(aXml->findChild("Commands")) {
		aXml->stepIn();
		while(aXml->findChild("Command")) {
			const string& name = aXml->getChildAttrib("Name");
			const string& command = aXml->getChildAttrib("Command");
			const string& hub = aXml->getChildAttrib("Hub");
			const string& nick = aXml->getChildAttrib("Nick");
			if(nick.empty()) {
				// Old mainchat style command
				addUserCommand(UserCommand::TYPE_RAW, UserCommand::CONTEXT_CHAT | UserCommand::CONTEXT_SEARCH, 
					0, name, "<%[mynick]> " + command + "|", hub);
			} else {
				addUserCommand(UserCommand::TYPE_RAW, UserCommand::CONTEXT_CHAT | UserCommand::CONTEXT_SEARCH,
					0, name, "$To: " + nick + " From: %[mynick] $" + command + "|", hub);
			}
		}
		aXml->stepOut();
	}
	// End old names

	aXml->resetCurrentChild();
	if(aXml->findChild("Hubs")) {
		aXml->stepIn();
		while(aXml->findChild("Hub")) {
			FavoriteHubEntry* e = new FavoriteHubEntry();
			e->setName(aXml->getChildAttrib("Name"));
			e->setConnect(aXml->getBoolChildAttrib("Connect"));
			e->setDescription(aXml->getChildAttrib("Description"));
			e->setNick(aXml->getChildAttrib("Nick"));
			e->setPassword(aXml->getChildAttrib("Password"));
			e->setServer(aXml->getChildAttrib("Server"));
			e->setUserDescription(aXml->getChildAttrib("UserDescription"));
			// CDM EXTENSION BEGINS FAVS
			//Add to favs
			e->setRawOne(aXml->getChildAttrib("RawOne"));
			e->setRawTwo(aXml->getChildAttrib("RawTwo"));
			e->setRawThree(aXml->getChildAttrib("RawThree"));
			e->setRawFour(aXml->getChildAttrib("RawFour"));
			e->setRawFive(aXml->getChildAttrib("RawFive"));
			e->setRawSix(aXml->getChildAttrib("RawSix"));
			e->setRawSeven(aXml->getChildAttrib("RawSeven"));
			e->setRawEight(aXml->getChildAttrib("RawEight"));
			e->setRawNine(aXml->getChildAttrib("RawNine"));
			e->setRawTen(aXml->getChildAttrib("RawTen"));
			e->setOpChat(aXml->getChildAttrib("OpChat"));
			e->setUserIp(aXml->getBoolChildAttrib("UserIp"));
			e->setCheckClients(aXml->getBoolChildAttrib("CheckClients"));
			e->setCheckFilelists(aXml->getBoolChildAttrib("CheckFilelists"));
			e->setCheckFakeShare(aXml->getBoolChildAttrib("CheckFakeShare"));
			e->setCheckOnConnect(aXml->getBoolChildAttrib("CheckOnConnect"));
			//Gabberworld start
			e->setCountryCodeCheck(aXml->getChildAttrib("CountryCodeCheck"));
			e->setCountryCodeRaw(aXml->getIntChildAttrib("CountryCodeRaw"));
			//Sulan start
			e->setProtectedUsers(aXml->getChildAttrib("ProtectedUsers"));
			e->setHideShare(aXml->getBoolChildAttrib("HideShare"));
			//Sulan start 28
			e->setTrigger(aXml->getChildAttrib("Trigger"));
			//Sulan start svn86
			e->setLogChat(aXml->getBoolChildAttrib("LogChat"));
			//Sulan start svn101
			e->setColumnOrder(aXml->getChildAttrib("ColumnOrder"));
			e->setColumnWidth(aXml->getChildAttrib("ColumnWidth"));
			e->setColumnVisible(aXml->getChildAttrib("ColumnVisible"));
			//Sulan start svn124
			e->setWindowPosX(aXml->getIntChildAttrib("WindowPosX"));
			e->setWindowPosY(aXml->getIntChildAttrib("WindowPosY"));
			e->setWindowSizeX(aXml->getIntChildAttrib("WindowSizeX"));
			e->setWindowSizeY(aXml->getIntChildAttrib("WindowSizeY"));
			e->setWindowType(aXml->getIntChildAttrib("WindowType"));
			//End
			// pothead starts svn192 svn196 (sulan code)
			e->setHubMessage(aXml->getChildAttrib("HubMessage"));
			e->setUseMyinfoDetect(aXml->getBoolChildAttrib("UseMyinfoDetect"));
			e->setDisableSearchSelect(aXml->getBoolChildAttrib("DisableSearchSelect"));
			e->setRegStatus(aXml->getChildAttrib("RegStatus"));
			e->setDisableRaws(aXml->getChildAttrib("DisableRaw"));
			// pothead ends
			// CDM EXTENSION ENDS
			favoriteHubs.push_back(e);
		}
		aXml->stepOut();
	}
	aXml->resetCurrentChild();
	if(aXml->findChild("Users")) {
		aXml->stepIn();
		while(aXml->findChild("User")) {
			User::Ptr u = ClientManager::getInstance()->getUser(aXml->getChildAttrib("Nick"), aXml->getChildAttrib("LastHubAddress"));
			if(!u->isOnline()) {
				u->setLastHubAddress(aXml->getChildAttrib("LastHubAddress"));
				u->setLastHubName(aXml->getChildAttrib("LastHubName"));
			}
			addFavoriteUser(u);
			u->setFavoriteGrantSlot(aXml->getBoolChildAttrib("GrantSlot"));
			u->setFavoriteLastSeen((u_int32_t)aXml->getIntChildAttrib("LastSeen"));
			u->setUserDescription(aXml->getChildAttrib("UserDescription"));
		}
		aXml->stepOut();
	}
	aXml->resetCurrentChild();
	if(aXml->findChild("UserCommands")) {
		aXml->stepIn();
		while(aXml->findChild("UserCommand")) {
			addUserCommand(aXml->getIntChildAttrib("Type"), aXml->getIntChildAttrib("Context"),
				0, aXml->getChildAttrib("Name"), aXml->getChildAttrib("Command"), aXml->getChildAttrib("Hub"));
		}
		aXml->stepOut();
	}
	dontSave = false;
}

void HubManager::refresh() {
	StringList l = StringTokenizer<string>(SETTING(HUBLIST_SERVERS), ';').getTokens();
	const string& server = l[(lastServer) % l.size()];
	if(Util::strnicmp(server.c_str(), "http://", 7) != 0) {
		lastServer++;
		return;
	}

	fire(HubManagerListener::DOWNLOAD_STARTING, server);
	if(!running) {
		if(!c)
			c = new HttpConnection();
		{
			Lock l(cs);
			publicHubs.clear();
		}
		c->addListener(this);
		c->downloadFile(server);
		running = true;
	}
}

UserCommand::List HubManager::getUserCommands(int ctx, const string& hub, bool op) {
	Lock l(cs);
	UserCommand::List lst;
	for(UserCommand::Iter i = userCommands.begin(); i != userCommands.end(); ++i) {
		UserCommand& uc = *i;
		if(uc.getCtx() & ctx) {
			if( (uc.getHub().empty()) || 
				(op && uc.getHub() == "op") || 
				(Util::stricmp(hub, uc.getHub()) == 0) )
			{
				lst.push_back(*i);
			}
		}
	}
	return lst;
}

// HttpConnectionListener
void HubManager::on(HttpConnectionListener::Data, HttpConnection* /*conn*/, const u_int8_t* buf, int len) throw() {
	downloadBuf.append((char*)buf, len);
	//dcassert(0);
}
void HubManager::on(HttpConnectionListener::Redirected, HttpConnection* /*conn*/, const string& aLine) throw() {
	fire(HubManagerListener::DOWNLOAD_STARTING, aLine);
}
void HubManager::on(HttpConnectionListener::Complete, HttpConnection* /*conn*/, const string& aLine) throw() {
	dcassert(c);
	c->removeListener(this);
	onHttpFinished();
	running = false;
	fire(HubManagerListener::DOWNLOAD_FINISHED, aLine);
}
void HubManager::on(HttpConnectionListener::Failed, HttpConnection* /*conn*/, const string& aLine) throw() {
	dcassert(c);
	c->removeListener(this);
	lastServer++;
	running = false;
	fire(HubManagerListener::DOWNLOAD_FAILED, aLine);
}
void HubManager::on(HttpConnectionListener::TypeBZ2, HttpConnection* /*conn*/) throw() {
	listType = TYPE_BZIP2;
}
void HubManager::on(HttpConnectionListener::TypeNormal, HttpConnection* /*conn*/) throw() {
	listType = TYPE_NORMAL;
}
void HubManager::on(SettingsManagerListener::Load, SimpleXML* xml) throw() {
	load(xml);
	previewload(xml);
}
void HubManager::on(SettingsManagerListener::Save, SimpleXML* xml) throw() {
	previewsave(xml);
}
// pothead starts svn185 (PDC code)
void HubManager::previewload(SimpleXML* aXml){
	WLock l(css);
	aXml->resetCurrentChild();
	if(aXml->findChild("PreviewApps")) {
		aXml->stepIn();
		while(aXml->findChild("Application")) {					
			addPreviewApp(aXml->getChildAttrib("Name"), aXml->getChildAttrib("Application"), 
				aXml->getChildAttrib("Arguments"), aXml->getChildAttrib("Extension"));			
		}
		aXml->stepOut();
	}	
}

void HubManager::previewsave(SimpleXML* aXml){
	RLock l(css);
	aXml->addTag("PreviewApps");
	aXml->stepIn();
	for(PreviewApplication::Iter i = previewApplications.begin(); i != previewApplications.end(); ++i) {
		aXml->addTag("Application");
		aXml->addChildAttrib("Name", (*i)->getName());
		aXml->addChildAttrib("Application", (*i)->getApplication());
		aXml->addChildAttrib("Arguments", (*i)->getArguments());
		aXml->addChildAttrib("Extension", (*i)->getExtension());
	}
	aXml->stepOut();
}
// pothead ends

/**
 * @file
 * $Id: HubManager.cpp 329 2005-08-30 21:06:02Z Pothead $
 */
#include "ClientProfileManager.h"
string HubManager::iCantAccessClientProfileManagerFromHereForSomeFuckingReasonThatIsWhyImUsingThisUglyHack(int i)
{
	return ClientProfileManager::getInstance()->getRawName(i);
}